/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


//! CHECKER      Inline global property based on IC, deoptimize if it is deleted or becomes accessor
//! RUN          options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::get_.*", entry: "_GLOBAL::func_main_0"
//! EVENT        /Deoptimization,_GLOBAL::func_get_x/
//! EVENT        /Deoptimization,_GLOBAL::func_get_y/
//! EVENT        /Deoptimization,_GLOBAL::func_get_may_throw/
//! METHOD       "get_x"
//! PASS_AFTER   "IrBuilder"
//! INST         "Intrinsic.TryLdGlobalByName"
//! PASS_AFTER   "InlineIntrinsics"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! INST         "GetGlobalVarAddress"
//! INST_NEXT    "LoadObject"
//! INST_NEXT    "Compare"
//! INST_NEXT    "DeoptimizeIf"
//! METHOD       "get_y"
//! PASS_AFTER   "IrBuilder"
//! INST         "Intrinsic.TryLdGlobalByName"
//! PASS_AFTER   "InlineIntrinsics"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! INST         "GetGlobalVarAddress"
//! INST_NEXT    "LoadObject"
//! INST_NEXT    "Compare"
//! INST_NEXT    "DeoptimizeIf"
//! METHOD       "get_non_conf"
//! PASS_AFTER   "IrBuilder"
//! INST         "Intrinsic.TryLdGlobalByName"
//! PASS_AFTER   "InlineIntrinsics"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! INST_NOT     "DeoptimizeIf"
//! INST         "GetGlobalVarAddress"
//! INST_NEXT    "LoadObject"
//! METHOD       "get_non_conf_twice"
//! PASS_AFTER   "IrBuilder"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! PASS_AFTER   "Inline."
//! INST         "Intrinsic.TryLdGlobalByName"
//! PASS_AFTER   "InlineIntrinsics"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! INST_NOT     "DeoptimizeIf INLINE_IC"
//! INST         "GetGlobalVarAddress"
//! INST_NEXT    "LoadObject"
//! METHOD       "get_const"
//! PASS_AFTER   "IrBuilder"
//! INST         "Intrinsic.TryLdGlobalByName"
//! INST_COUNT   "Constant ", 1
//! PASS_AFTER   "InlineIntrinsics"
//! INST_NOT     "Intrinsic.TryLdGlobalByName"
//! INST_NOT     "DeoptimizeIf"
//! INST_NOT     "GetGlobalVarAddress"
//! INST_COUNT   "Constant ", 2
//! METHOD       "get_always_throw"
//! PASS_AFTER   "Codegen"
//! INST         "Intrinsic.TryLdGlobalByName"

function get_x() {
    return x;
}
function get_y() {
    return y;
}
function get_non_conf() {
    return non_conf_var;
}
function inlined_get_non_conf() {
    return non_conf_var;
}
function get_non_conf_twice() {
    return inlined_get_non_conf() + inlined_get_non_conf();
}
function get_const() {
    return const_var;
}
function get_may_throw() {
    return may_throw;
}
function get_always_throw() {
    return always_throw;
}

Object.defineProperty(globalThis, "x", {value: 1, writable: true, configurable: true});
var sum = 0;
for (var i = 0; i < 20; i++) {
    sum += get_x();
}
if (sum != 20) {
    throw "Wrong result for global field: " + sum;
}
var getter_calls = 0;
Object.defineProperty(globalThis, "x", {get: function() { getter_calls++; return 2; }});
for (var i = 0; i < 20; i++) {
    sum += get_x();
}
if (getter_calls != 20) {
    throw "Wrong number of getter calls: " + getter_calls;
}
if (sum != 60) {
    throw "Wrong result after transition to accessor: " + sum;
}

delete globalThis.x;
try {
    get_x();
    throw "did't get reference error for deleted property";
} catch (e) {
    if (!(e instanceof ReferenceError)) {
        throw e;
    }
}

globalThis.y = 1;
sum = 0;
for (var i = 0; i < 20; i++) {
    sum += get_y();
}
delete globalThis.y;
try {
    get_y();
    throw "did't get reference error for deleted property";
} catch (e) {
    if (!(e instanceof ReferenceError)) {
        throw e;
    }
}
Object.defineProperty(globalThis, "y", {value: 2, writable: true, configurable: true});
for (var i = 0; i < 20; i++) {
    sum += get_y();
}
if (sum != 60) {
    throw "Wrong result for deleted and redefined property: " + sum;
}

Object.defineProperty(globalThis, "non_conf_var", {value: 1, writable: true, configurable: false});
sum = 0;
for (var i = 0; i < 20; i++) {
    sum += get_non_conf();
}
if (sum != 20) {
    throw "Wrong result for non-configurable global var: " + sum;
}
non_conf_var = -1;
for (var i = 0; i < 20; i++) {
    sum += get_non_conf();
}
if (sum != 0) {
    throw "Wrong result after write to non-configurable global var: " + sum;
}

sum = 0;
for (var i = 0; i < 15; i++) {
    sum += get_non_conf_twice();
}
if (sum != -30) {
    throw "Wrong result for loading non-configurable global var from inlined function: " + sum;
}

Object.defineProperty(globalThis, "const_var", {value: 4, writable: false, configurable: false});
sum = 0;
for (var i = 0; i < 20; i++) {
    sum += get_const();
}
if (sum != 80) {
    throw "Wrong result for non-writable global var: " + sum;
}

Object.defineProperty(globalThis, "may_throw", {value: 5, configurable: true});
sum = 0;
for (var i = 0; i < 20; i++) {
    sum += get_may_throw();
}
if (sum != 100) {
    throw "Wrong result for configurable global var: " + sum;
}
Object.defineProperty(globalThis, "may_throw", {get: function() { throw 11; }, configurable: true});
try {
    get_may_throw();
    throw "Did't get exception from getter";
} catch (e) {
    if (e != 11) {
        throw "Getter has thrown wrong exception: " + e;
    }
}

Object.defineProperty(globalThis, "always_throw", {get: function() { throw 1; }, configurable: false});
sum = 0;
for (var i = 0; i < 15; i++) {
    try {
        sum -= get_always_throw();
    } catch (e) {
        sum += e;
    }
}
if (sum != 15) {
    throw "Wrong result from non-configurable global property with throwing getter: " + sum;
}